import os
import logging
from typing import List, Optional, Generator
from concurrent.futures import ThreadPoolExecutor, as_completed

from app.models import Comic, Chapter, Page, ComicLibrary
from app.utils import (
    is_image_file, sort_files_naturally, sort_paths_naturally,
    extract_title_from_path, find_cover_image, validate_comic_directory,
    normalize_path, is_subdirectory
)

logger = logging.getLogger(__name__)

class DirectoryScanner:
    """目录扫描服务"""
    
    def __init__(self, app=None):
        self.app = app
        self.comic_library = ComicLibrary()
        self.comic_path = ""
        self.scan_depth = 3  # 默认扫描深度
        self.max_workers = 4  # 并发扫描线程数
        
        if app:
            self.init_app(app)
    
    def init_app(self, app):
        """初始化应用"""
        self.app = app
        self.comic_path = app.config.get('COMIC_PATH', '')
        self.scan_depth = app.config.get('SCAN_DEPTH', 3)
        self.max_workers = app.config.get('SCAN_MAX_WORKERS', 4)
        
        # 确保漫画目录存在
        if self.comic_path and not os.path.exists(self.comic_path):
            try:
                os.makedirs(self.comic_path, exist_ok=True)
                logger.info(f"创建漫画目录: {self.comic_path}")
            except OSError as e:
                logger.error(f"无法创建漫画目录 {self.comic_path}: {e}")
    
    def scan_comics(self, force_refresh: bool = False) -> ComicLibrary:
        """扫描漫画目录"""
        if not self.comic_path or not os.path.exists(self.comic_path):
            logger.warning(f"漫画目录不存在: {self.comic_path}")
            return self.comic_library
        
        if force_refresh:
            self.comic_library.clear()
        
        logger.info(f"开始扫描漫画目录: {self.comic_path}")
        
        try:
            # 找到所有可能的漫画目录
            comic_directories = list(self._find_comic_directories(self.comic_path))
            logger.info(f"找到 {len(comic_directories)} 个漫画目录")
            
            # 并发扫描漫画
            with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                future_to_path = {
                    executor.submit(self._scan_single_comic, path): path 
                    for path in comic_directories
                }
                
                for future in as_completed(future_to_path):
                    path = future_to_path[future]
                    try:
                        comic = future.result()
                        if comic:
                            self.comic_library.add_comic(comic)
                            logger.debug(f"扫描完成: {comic.title}")
                    except Exception as e:
                        logger.error(f"扫描漫画失败 {path}: {e}")
            
            logger.info(f"漫画扫描完成，共 {len(self.comic_library.comics)} 部漫画")
            
        except Exception as e:
            logger.error(f"扫描过程中发生错误: {e}")
        
        return self.comic_library
    
    def _find_comic_directories(self, root_path: str, current_depth: int = 0) -> Generator[str, None, None]:
        """递归查找漫画目录"""
        if current_depth > self.scan_depth:
            return
        
        try:
            if not os.path.isdir(root_path):
                return
            
            # 检查当前目录是否包含图片文件
            has_images = False
            has_subdirs = False
            
            items = os.listdir(root_path)
            for item in items:
                item_path = os.path.join(root_path, item)
                
                if os.path.isfile(item_path) and is_image_file(item):
                    has_images = True
                elif os.path.isdir(item_path):
                    has_subdirs = True
            
            # 如果当前目录有图片文件，则认为是漫画目录
            if has_images:
                yield root_path
            
            # 如果有子目录且没有图片文件，继续递归
            if has_subdirs and not has_images:
                subdirs = [
                    os.path.join(root_path, item) 
                    for item in items 
                    if os.path.isdir(os.path.join(root_path, item))
                ]
                
                # 对子目录进行自然排序
                subdirs = sort_paths_naturally(subdirs)
                
                for subdir in subdirs:
                    yield from self._find_comic_directories(subdir, current_depth + 1)
                        
        except (OSError, PermissionError) as e:
            logger.warning(f"无法访问目录 {root_path}: {e}")
    
    def _scan_single_comic(self, comic_path: str) -> Optional[Comic]:
        """扫描单个漫画目录"""
        try:
            # 验证目录
            is_valid, message = validate_comic_directory(comic_path)
            if not is_valid:
                logger.warning(f"无效的漫画目录 {comic_path}: {message}")
                return None
            
            # 创建漫画对象
            comic_title = extract_title_from_path(comic_path)
            comic = Comic(
                id="",  # 将在__post_init__中生成
                title=comic_title,
                path=normalize_path(comic_path)
            )
            
            # 扫描章节
            chapters = self._scan_chapters(comic_path)
            for chapter in chapters:
                comic.add_chapter(chapter)
            
            # 设置封面图片
            comic.cover_image = find_cover_image(comic_path)
            
            return comic
            
        except Exception as e:
            logger.error(f"扫描漫画失败 {comic_path}: {e}")
            return None
    
    def _scan_chapters(self, comic_path: str) -> List[Chapter]:
        """扫描章节"""
        chapters = []
        
        try:
            # 检查是否有子目录（多章节结构）
            items = os.listdir(comic_path)
            subdirs = [
                item for item in items 
                if os.path.isdir(os.path.join(comic_path, item))
            ]
            
            if subdirs:
                # 多章节结构
                subdirs = sort_files_naturally(subdirs)
                
                for subdir in subdirs:
                    chapter_path = os.path.join(comic_path, subdir)
                    
                    # 验证章节目录
                    is_valid, _ = validate_comic_directory(chapter_path)
                    if not is_valid:
                        continue
                    
                    chapter = self._create_chapter(chapter_path, subdir)
                    if chapter:
                        chapters.append(chapter)
            else:
                # 单章节结构（直接包含图片文件）
                chapter = self._create_chapter(comic_path, "第一话")
                if chapter:
                    chapters.append(chapter)
                    
        except OSError as e:
            logger.error(f"扫描章节失败 {comic_path}: {e}")
        
        return chapters
    
    def _create_chapter(self, chapter_path: str, chapter_name: str) -> Optional[Chapter]:
        """创建章节对象"""
        try:
            chapter_title = extract_title_from_path(chapter_name)
            chapter = Chapter(
                id="",  # 将在__post_init__中生成
                comic_id="",  # 将在添加到漫画时设置
                title=chapter_title,
                path=normalize_path(chapter_path)
            )
            
            # 扫描页面
            pages = self._scan_pages(chapter_path)
            for page in pages:
                chapter.add_page(page)
            
            return chapter if chapter.pages else None
            
        except Exception as e:
            logger.error(f"创建章节失败 {chapter_path}: {e}")
            return None
    
    def _scan_pages(self, chapter_path: str) -> List[Page]:
        """扫描页面"""
        pages = []
        
        try:
            files = os.listdir(chapter_path)
            image_files = [f for f in files if is_image_file(f)]
            
            if not image_files:
                return pages
            
            # 对图片文件进行自然排序
            image_files = sort_files_naturally(image_files)
            
            for page_number, filename in enumerate(image_files, 1):
                image_path = os.path.join(chapter_path, filename)
                
                page = Page(
                    id="",  # 将在__post_init__中生成
                    chapter_id="",  # 将在添加到章节时设置
                    page_number=page_number,
                    image_path=normalize_path(image_path)
                )
                
                pages.append(page)
                
        except OSError as e:
            logger.error(f"扫描页面失败 {chapter_path}: {e}")
        
        return pages
    
    def get_comic_by_path(self, path: str) -> Optional[Comic]:
        """根据路径获取漫画"""
        normalized_path = normalize_path(path)
        
        for comic in self.comic_library.comics:
            if comic.path == normalized_path:
                return comic
        
        return None
    
    def refresh_comic(self, comic_id: str) -> Optional[Comic]:
        """刷新单个漫画"""
        comic = self.comic_library.get_comic(comic_id)
        if not comic:
            return None
        
        # 重新扫描漫画目录
        refreshed_comic = self._scan_single_comic(comic.path)
        if refreshed_comic:
            # 更新库中的漫画
            for i, existing_comic in enumerate(self.comic_library.comics):
                if existing_comic.id == comic_id:
                    self.comic_library.comics[i] = refreshed_comic
                    self.comic_library._comic_index[comic_id] = refreshed_comic
                    break
            
            return refreshed_comic
        
        return None
    
    def add_comic_path(self, path: str) -> Optional[Comic]:
        """添加新的漫画路径"""
        if not os.path.exists(path):
            return None
        
        # 检查路径是否在允许的范围内
        if not is_subdirectory(path, self.comic_path):
            logger.warning(f"路径不在允许范围内: {path}")
            return None
        
        # 扫描新漫画
        comic = self._scan_single_comic(path)
        if comic:
            self.comic_library.add_comic(comic)
            logger.info(f"添加新漫画: {comic.title}")
        
        return comic
    
    def get_library_stats(self) -> dict:
        """获取库统计信息"""
        total_comics = len(self.comic_library.comics)
        total_chapters = sum(comic.total_chapters for comic in self.comic_library.comics)
        total_pages = sum(comic.total_pages for comic in self.comic_library.comics)
        
        return {
            'total_comics': total_comics,
            'total_chapters': total_chapters,
            'total_pages': total_pages,
            'comic_path': self.comic_path
        }

# 全局实例
directory_scanner = DirectoryScanner()